﻿

namespace Graph.Algorithm.Core
{
    public class QuantGeneticAlgorithm
    {

        #region builder

        private static Random randomBuilder { get; set; } = new Random();
        private static VectorBuilder<double> vectorBuilder { get; set; } = Vector<double>.Build;
        //private static MatrixBuilder<double> matBuilder { get; set; } = Matrix<double>.Build;

        #endregion

        #region data

        /// <summary>
        /// 量子群
        /// </summary>
        public static List<Quant[]> sequences { get; set; } = new List<Quant[]>();
        /// <summary>
        /// 当前最佳量子
        /// </summary>
        public static Quant[] bestSequence { get; set; }

        /// <summary>
        /// 旋转门查询表
        /// </summary>
        public static Dictionary<string, double> thetaDict { get; set; } = new Dictionary<string, double>();

        /// <summary>
        /// 目标函数 适应度函数
        /// </summary>
        public static Func<double[], double> fitnessFunc { get; set; } = (q) => { return 1; };


        private static int sequenceLen { get; set; }
        #endregion


        public QuantGeneticAlgorithm(int _sequenceLen, Func<double[], double> _fitnessFunc)
        {
            fitnessFunc = _fitnessFunc;
            sequenceLen = _sequenceLen;

            thetaDict = GenThetaStrDict();

            //建立量子群
            for (int i = 0; i < 50; i++)
            {
                List<Quant> sequence = new List<Quant>();
                for (int j = 0; j < _sequenceLen; j++)
                {
                    var quant = new Quant();
                    quant.id = new int[] { i, j };
                    sequence.Add(quant);
                }
                sequences.Add(sequence.ToArray());
            }
        }

        public static Dictionary<string, double> GenThetaStrDict()
        {
            var sign = Math.Sign(randomBuilder.NextDouble() - 0.5d);

            Dictionary<string, double> pairs = new Dictionary<string, double>();

            pairs.Add("0000", 0);
            pairs.Add("0010", 0);

            //xi  besti  fg  ab>0  a=0
            pairs.Add("0101", +1d);
            pairs.Add("0102", -1d);
            pairs.Add("0103", 0d);
            pairs.Add("0104", -1d * sign);

            pairs.Add("0111", -1d);
            pairs.Add("0112", 1d);
            pairs.Add("0113", 1d * sign);
            pairs.Add("0114", 0d);

            pairs.Add("1001", -1d);
            pairs.Add("1002", 1d);
            pairs.Add("1003", 1d * sign);
            pairs.Add("1004", 0d);

            pairs.Add("1011", 1d);
            pairs.Add("1012", -1d);
            pairs.Add("1013", 0d);
            pairs.Add("1014", 1d * sign);

            pairs.Add("1100", 0);
            pairs.Add("1110", 0);


            return pairs;
        }

        public void Run(int numberOfGeneration)
        {
            while (numberOfGeneration > 0)
            {
                numberOfGeneration--;

                //寻最优 
                foreach (var sequence in sequences)
                {
                    sequence.ToList().ForEach(q => q.Collapse());
                    Console.WriteLine(string.Join("", sequence.Select(s => s.collapseValue)));
                }

                var minV = double.MaxValue;
                foreach (var sequence in sequences)
                {
                    var tag = fitnessFunc(SequenceToDoubleArray(sequence));
                    Console.WriteLine(tag);
                    if (tag < minV) { minV = tag; bestSequence = sequence; }
                }
                //sequences = sequences.OrderBy(s => fitnessFunc(SequenceToDoubleArray(s))).ToList();
                //bestSequence = sequences.First();

                //开始量子自旋变异
                sequences = sequences.Select(s => QuantRotate(s)).ToList();
            }

        }


        public double[] GetResult()
        {
            Console.WriteLine($"{bestSequence}");
            double[] xy = SequenceToDoubleArray(bestSequence);
            return xy;
        }

        #region Featrue

        static double[] SequenceToDoubleArray(Quant[] sequence)
        {
            int byteNum = sequenceLen/2;

            var xBinary = sequence.Select(s => s.collapseValue)
                                .ToList()
                                .GetRange(0, byteNum);

            double x = Convert.ToInt32(string.Join("", xBinary), 2);

            //var x = 0f;
            //string.Join("", xBinary).BinaryToFloat32(out x);
            //=====================================================
            var yBinary = sequence.Select(s => s.collapseValue)
                               .ToList()
                               .GetRange(32, byteNum);
            //var y = 0f;
            //string.Join("", yBinary).BinaryToFloat32(out y);
            double y = Convert.ToInt32(string.Join("", yBinary), 2);
            //=====================================================

            //List<double> results = new List<double>() { x, y };

            return new double[] { x * 0.001, y * 0.001 };
        }



        /// <summary>
        /// 量子旋转门
        /// </summary>
        /// <param name="sequence"></param>
        /// <returns></returns>
        public static Quant[] QuantRotate(Quant[] sequence)
        {
            List<Quant> quants = new List<Quant>();

            var thetas = GenTheta(sequence, bestSequence);
            for (int i = 0; i < sequence.Length; i++)
            {
                var quant = sequence[i];
                var theta = thetas[i] * 0.01 * Math.PI;

                sequence[i].alpha = quant.alpha * Math.Cos(theta) - quant.beta * Math.Sin(theta);
                sequence[i].beta = quant.alpha * Math.Sin(theta) + quant.beta * Math.Cos(theta);
            }

            return sequence;
        }

        public static double[] GenTheta(Quant[] currQuant, Quant[] bestQuant)
        {
            List<double> thetas = new List<double>();

            for (int i = 0; i < currQuant.Length; i++)
            {
                var currQ = currQuant[i];
                var bestQ = bestQuant[i];

                int xi = currQ.collapseValue;
                int bi = bestQ.collapseValue;
                int fxfb =
                    fitnessFunc(SequenceToDoubleArray(currQuant)) -
                    fitnessFunc(SequenceToDoubleArray(bestQuant)) > 0 ? 1 : 0;

                int sign = 0;
                sign = currQ.alpha * currQ.beta > 0d ? 1 : 0;
                sign = currQ.alpha * currQ.beta < 0d ? 2 : 0;
                sign = currQ.alpha == 0 ? 3 : 0;
                sign = currQ.beta == 0 ? 4 : 0;

                var arr = new int[4];
                arr.Append(xi);
                arr.Append(bi);
                arr.Append(fxfb);
                arr.Append(sign);

                string arrStr = string.Join("", arr);
                //var theta = thetaDict.Where(d => d.Key == arrStr).First().Value;
                thetas.Add(thetaDict[arrStr]);
            }
            return thetas.ToArray();
        }

        #endregion

        //=============================================
        /// <summary>
        /// 单个量子
        /// </summary>
        public class Quant
        {
            /// <summary>
            /// 默认等概率
            /// </summary>
            public Quant()
            {
                this.id = new int[] { 0, 0 };
                this.alpha = 1d / Math.Sqrt(2);
                this.beta = GetBeta();
            }
            /// <summary>
            /// 给定实轴数据
            /// </summary>
            /// <param name="_alpha"></param>
            public Quant(double _alpha)
            {
                this.id = new int[] { 0, 0 };
                this.alpha = _alpha;
                this.beta = GetBeta();
            }

            //public Quant(Vector<double> vec)
            //{
            //    this.id = 0;
            //    this.alpha = vec[0];
            //    this.beta = vec[1];
            //}

            /// <summary>
            /// 量子测量塌缩
            /// </summary>
            /// <returns></returns>
            public int Collapse()
            {
                var tag = randomBuilder.NextDouble();
                var res = Math.Pow(this.alpha, 2) > tag ? 0 : 1;
                this.collapseValue = res;
                return res;
            }

            public double GetAlpha()
            {
                return Math.Sqrt(1 - this.beta * this.beta);
            }
            public double GetBeta()
            {
                return Math.Sqrt(1 - this.alpha * this.alpha);
            }

            public Vector<double> ToVector()
            {
                var vec = vectorBuilder.Dense(2);
                vec[0] = this.alpha;
                vec[1] = this.beta;
                return vec;
            }

            /// <summary>
            /// 量子编码
            /// </summary>
            public int[] id { get; set; }
            public double alpha { get; set; }
            public double beta { get; set; }

            public int collapseValue { get; set; }
        }



    }
}
